﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace UniUI.ConsoleUI
{
    /// <summary>
    /// Base class for every object that appears on the Screen.
    /// </summary>
    public interface IConsoleControl
    {
        /// <summary>
        /// Get the parent control that contains this control.
        /// Return null if it's the top level control in the Window (UniUI.ConsoleUI.ConsoleWindow).
        /// </summary>
        IConsoleControl ParentControl { get; }

        /// <summary>
        /// Get the position of the left border.
        /// Relative to the ParentControl. Use GetAbsoluteLeft(ConsoleControlExt.GetAbsoluteLeft) to get absolute position.
        /// </summary>
        int Left { get; }

        /// <summary>
        /// Get the position of the top border.
        /// Relative to the ParentControl. Use GetAbsoluteLeft(ConsoleControlExt.GetAbsoluteTop) to get absolute position.
        /// </summary>
        int Top { get; }

        /// <summary>
        /// Get the width of this control.
        /// </summary>
        int Width { get; }

        /// <summary>
        /// Get the height of this control.
        /// </summary>
        int Height { get; }

        /// <summary>
        /// Render the control onto the given screen.
        /// </summary>
        /// <param name="screen">The ConsoleScreen object that is rendered onto.</param>
        void Render(ConsoleScreen screen);

        /// <summary>
        /// Set a region on this control specified by the parameters, to be invalid.
        /// Invalid region should be marked to be re-rendered next time.
        /// </summary>
        /// <param name="left">The left of the region.</param>
        /// <param name="top">The top of the region.</param>
        /// <param name="width">The width of the region.</param>
        /// <param name="height">The height of the region.</param>
        void Invalidate(int left, int top, int width, int height);
    }

    /// <summary>
    /// Implement the extension method of IConsoleControl interface.
    /// </summary>
    public static class ConsoleControlExt
    {
        /// <summary>
        /// Calculate the absolute left position of the given control.
        /// </summary>
        /// <param name="ctrl">The control that is calculated.</param>
        /// <returns></returns>
        public static int GetAbsoluteLeft(this IConsoleControl ctrl)
        {
            if (ctrl.ParentControl == null)
            {
                return ctrl.Left;
            }
            return ctrl.Left + ctrl.ParentControl.GetAbsoluteLeft();
        }

        /// <summary>
        /// Calculate the absolute top position of the given control.
        /// </summary>
        /// <param name="ctrl">The control that is calculated.</param>
        /// <returns></returns>
        public static int GetAbsoluteTop(this IConsoleControl ctrl)
        {
            if (ctrl.ParentControl == null)
            {
                return ctrl.Top;
            }
            return ctrl.Top + ctrl.ParentControl.GetAbsoluteTop();
        }

        /// <summary>
        /// Invalidate the parent control of the given control, with the exact region that covers the given control.
        /// Will also invalidate the control itself.
        /// </summary>
        /// <param name="ctrl">The given control whose parent is to be invalidated.</param>
        public static void InvalidateParent(this IConsoleControl ctrl)
        {
            //invalidate itself
            ctrl.Invalidate();

            //invalidate the parent
            if (ctrl.ParentControl == null)
            {
                return;
            }
            ctrl.ParentControl.Invalidate(ctrl.Left, ctrl.Top, ctrl.Width, ctrl.Height);
        }

        /// <summary>
        /// Invalidate the whole region of the given control.
        /// </summary>
        /// <param name="ctrl">The given control that is to be invalidated.</param>
        public static void Invalidate(this IConsoleControl ctrl)
        {
            ctrl.Invalidate(0, 0, ctrl.Width, ctrl.Height);
        }
    }
}
